"""
SAR策略实盘交易脚本

功能：
1. 从策略配置获取要运行的策略，其中包含多个币种，各币种独立运行
2. 从交易所获取100根K线做第一次信号判断
3. 订阅K线频道，始终保持100根K线数据，去除旧的数据，打印最新的2根K线
4. 每次更新完K线数据后进行开仓/平仓信号判断，并输出详细判断数据
"""

import os
import sys
import time
import json
import asyncio
import logging
import argparse
import pandas as pd
import re
import ast
from datetime import datetime, timedelta
from typing import Dict, List, Any, Optional
import threading
import queue
from sqlalchemy import select
from sqlalchemy.orm import Session
from pathlib import Path

# 添加项目根目录到Python路径
project_root = Path(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
sys.path.append(str(project_root))

from models.base import SessionLocal
from models.strategy_config import StrategyConfig
from models.user_api_key import UserApiKey
from models.strategy import Strategy, Trade
from services.exchange.adapters.gate_adapter import GateAdapter
from services.exchange import ExchangeType, KLineInterval
from services.strategies.implementations.sar_strategy import SARStrategy
from utils.dingtalk import send_dingtalk_message
# 使用合约配置适配器
from services.contract_config_adapter import get_mark_price_round, get_quanto_multiplier, get_contract_info, format_price
# 导入新的K线订阅器
from services.exchange.kline_subscriber import KlineSubscriber

# 创建基本日志配置
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    handlers=[
        logging.StreamHandler(sys.stdout)
    ]
)
logger = logging.getLogger("sar_realtime")

def setup_logger(symbol: str) -> logging.Logger:
    """
    为特定交易对设置日志记录器
    
    参数:
        symbol: 交易对符号
    
    返回:
        配置好的日志记录器
    """
    # 创建基础日志目录结构
    symbol_safe = symbol.replace('-', '_')
    date_str = datetime.now().strftime('%Y%m%d')
    hour_str = datetime.now().strftime('%H')
    
    # 创建日志目录
    log_dir = Path(f"logs/sar/{symbol_safe}/{date_str}")
    log_dir.mkdir(parents=True, exist_ok=True)
    
    # 创建日志文件路径
    log_file = log_dir / f"{hour_str}.log"
    
    # 创建特定交易对的日志记录器
    symbol_logger = logging.getLogger(f"sar_realtime.{symbol}")
    
    # 避免日志重复
    if not symbol_logger.handlers:
        # 添加文件处理器，指定UTF-8编码
        file_handler = logging.FileHandler(log_file, encoding='utf-8')
        file_handler.setFormatter(logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s'))
        symbol_logger.addHandler(file_handler)
        
        # 继承主日志级别
        symbol_logger.setLevel(logger.level)
    
    return symbol_logger

class SARTradingBot:
    """SAR实盘交易机器人"""
    
    def __init__(self, symbol: str, strategy_params: Dict[str, Any], use_testnet: bool = False):
        """
        初始化交易机器人
        
        参数:
            symbol: 交易对符号
            strategy_params: 策略参数
            use_testnet: 是否使用测试网
        """
        self.symbol = symbol
        self.strategy_params = strategy_params
        self.use_testnet = use_testnet
        
        # 设置币种特定的日志记录器
        self.logger = setup_logger(symbol)
        
        # 初始化交易所适配器
        self.exchange = GateAdapter(use_testnet=use_testnet)
        
        # 初始化K线订阅器 (使用新的KlineSubscriber类)
        self.kline_subscriber = KlineSubscriber(
            symbol=symbol, 
            exchange_adapter=self.exchange, 
            use_testnet=use_testnet,
            logger_name=self.logger
        )
        
        # API凭证
        self.api_credentials = None
        
        # 初始化策略
        self.strategy = SARStrategy(symbol=symbol, parameters=strategy_params)
        
        # 从配置文件获取合约信息，并设置到策略中
        mark_price_round = get_mark_price_round(ExchangeType.GATE, symbol)
        quanto_multiplier = get_quanto_multiplier(ExchangeType.GATE, symbol)
        
        if mark_price_round is not None:
            self.strategy.mark_price_round = float(mark_price_round)
        
        if quanto_multiplier is not None:
            self.strategy.quanto_multiplier = float(quanto_multiplier)
            
        self.logger.info(f"初始化策略: {symbol}, 价格精度: {self.strategy.mark_price_round}, 合约乘数: {self.strategy.quanto_multiplier}")
        
        # 交易状态
        self.long_position = False
        self.short_position = False
        self.entry_data = None
        
        # 最新的市场价格
        self.latest_price = None
        self.last_signal_check_time = None
        
        # 记录K线内交易状态，避免同一K线内重复交易
        self.current_candle_time = None
        self.current_candle_had_entry = False
        self.current_candle_had_exit = False
        
        self.logger.info(f"初始化 {symbol} 的SAR实盘交易机器人")

    async def initialize(self):
        """初始化机器人，加载历史K线数据"""
        try:
            # 使用K线订阅器初始化历史K线数据
            success = await self.kline_subscriber.initialize(limit=100)
            if not success:
                self.logger.error(f"初始化 {self.symbol} 的历史K线数据失败")
                return False
            
            # 获取K线数据并进行首次信号检测
            klines_data = self.kline_subscriber.get_klines_data()
            df = self.kline_subscriber.convert_klines_to_dataframe(klines_data)
            
            # 准备数据并进行首次信号检测
            await self.check_signals(df)
            
            return True
        except Exception as e:
            self.logger.error(f"初始化 {self.symbol} 机器人时出错: {e}", exc_info=True)
            return False
    
    async def get_api_credentials(self, user_id=None, exchange="gate"):
        """
        从数据库获取交易所API凭证
        
        参数:
            user_id: 用户ID，如果不指定则获取第一个符合条件的API密钥
            exchange: 交易所名称，默认为"gate"
            
        返回:
            包含api_key和api_secret的字典，如果没有找到则返回None
        """
        # 使用项目中定义的数据库会话
        session = SessionLocal()
        
        try:
            # 构建查询
            query = select(UserApiKey).where(UserApiKey.exchange == exchange)
            if user_id:
                query = query.where(UserApiKey.user_id == user_id)
            
            # 执行查询
            api_key_record = session.execute(query).scalar_one_or_none()
            
            if api_key_record:
                self.logger.info(f"找到用户 {api_key_record.user_id} 的 {exchange} API密钥")
                # 直接使用UserApiKey模型中的get_credentials方法
                return api_key_record.get_credentials()
            else:
                self.logger.error(f"未找到 {exchange} API密钥")
                return None
                
        except Exception as e:
            self.logger.error(f"获取API密钥时发生错误: {e}")
            return None
        finally:
            session.close()

    async def setup_trading(self):
        """设置交易环境，包括获取API密钥"""
        # 获取API凭证
        self.api_credentials = await self.get_api_credentials(1)
        if self.api_credentials:
            # 重新初始化交易所适配器，包含API密钥
            self.exchange = GateAdapter(
                api_key=self.api_credentials["api_key"],
                api_secret=self.api_credentials["api_secret"],
                use_testnet=self.use_testnet
            )
            
            # 更新K线订阅器中的交易所适配器
            self.kline_subscriber.exchange = self.exchange
            
            self.logger.info("已使用API密钥初始化交易所适配器")
            return True
        else:
            self.logger.error("未找到有效的API密钥，无法执行交易")
            return False

    async def place_order(self, side: str, size: float, price: float = 0, reduce_only: bool = False):
        """
        下单函数
        
        参数:
            side: 订单方向，"buy" 或 "sell"
            size: 仓位大小
            price: 价格，0表示市价单
            reduce_only: 是否只减仓
            
        返回:
            订单信息字典，下单失败返回None
        """
        if not self.api_credentials:
            self.logger.error("没有API凭证，无法下单")
            return None
            
        try:
            # 格式化交易对（将-替换为_）
            symbol_for_exchange = self.symbol.replace('-', '_')
            
            # 计算仓位大小（如果没有指定）
            if size == 0 and reduce_only:
                auto_size = "close_long" if side == "sell" else "close_short"
                
                order = await self.exchange.create_order(
                    symbol=symbol_for_exchange,
                    price=price,
                    side=side,
                    size=size,
                    text="t-sar-strategy",
                    auto_size=auto_size,
                    reduce_only=True
                )
            else:
                order = await self.exchange.create_order(
                    symbol=symbol_for_exchange,
                    price=price,
                    side=side,
                    size=size,
                    text="t-sar-strategy"
                )
                
            self.logger.info(f"下单成功: {order}")
            return order
        except Exception as e:
            self.logger.error(f"下单失败: {e}", exc_info=True)
            return None

    async def save_trade_to_db(self, order: Dict[str, Any], trade_type: str, reason: str, entry_trade_id: Optional[int] = None) -> Optional[int]:
        """
        将交易记录保存到数据库
        
        参数:
            order: 订单信息
            trade_type: 交易类型 entry/exit
            reason: 交易原因
            entry_trade_id: 开仓交易ID (仅平仓时使用)
            
        返回:
            交易记录ID，失败则返回None
        """
        if not order:
            self.logger.error("没有订单信息，无法保存交易记录")
            return None
            
        try:
            # 创建数据库会话
            session = SessionLocal()
            
            # 获取策略ID
            strategy = session.query(Strategy).filter(Strategy.code == "SARStrategy").first()
            if not strategy:
                # 如果策略不存在，创建策略
                strategy = Strategy(
                    name="SAR策略",
                    code="SARStrategy",
                    description="抛物线转向策略",
                    is_active=True
                )
                session.add(strategy)
                session.commit()
            
            # 获取填充价格和数量
            fill_price = str(order.get('fill_price', 0))
            size = str(order.get('size', 0))
            
            if trade_type == 'entry':
                # 创建开仓记录
                trade = Trade(
                    strategy_id=strategy.id,
                    symbol=self.symbol,
                    direction='long' if order.get('side') == 'buy' else 'short',
                    entry_price=fill_price,
                    entry_time=datetime.now(),
                    volume=size,
                    status="open",
                    signal_data=json.dumps({
                        "order_id": str(order.get('id', '')),
                        "reason": reason,
                        "details": order
                    })
                )
            elif trade_type == 'exit' and entry_trade_id:
                # 查找开仓记录
                entry_trade = session.query(Trade).filter(Trade.id == entry_trade_id).first()
                if entry_trade:
                    # 更新开仓记录为已平仓状态
                    entry_trade.exit_price = fill_price
                    entry_trade.exit_time = datetime.now()
                    entry_trade.status = "closed"
                    
                    # 计算盈亏
                    if entry_trade.direction == 'long':
                        # 多头盈亏 = (卖出价 - 买入价) * 数量
                        profit = (float(fill_price) - float(entry_trade.entry_price)) * float(size)
                    else:
                        # 空头盈亏 = (买入价 - 卖出价) * 数量
                        profit = (float(entry_trade.entry_price) - float(fill_price)) * float(size)
                    
                    entry_trade.profit = str(profit)
                    
                    # 更新信号数据
                    signal_data = json.loads(entry_trade.signal_data) if entry_trade.signal_data else {}
                    signal_data.update({
                        "exit_order_id": str(order.get('id', '')),
                        "exit_reason": reason,
                        "exit_details": order
                    })
                    entry_trade.signal_data = json.dumps(signal_data)
                    
                    trade = entry_trade
                else:
                    self.logger.warning(f"未找到对应的开仓记录 ID: {entry_trade_id}")
                    return None
            else:
                self.logger.warning(f"不支持的交易类型: {trade_type}")
                return None
            
            # 保存到数据库
            if trade_type == 'entry':
                session.add(trade)
            session.commit()
            
            # 获取交易记录ID
            trade_id = trade.id
            
            self.logger.info(f"交易记录已保存到数据库，ID: {trade_id}, 类型: {trade_type}")
            return trade_id
            
        except Exception as e:
            self.logger.error(f"保存交易记录失败: {e}", exc_info=True)
            return None
        finally:
            if session:
                session.close()

    async def check_signals(self, df: pd.DataFrame):
        """
        检查开仓和平仓信号
        
        参数:
            df: K线数据DataFrame
        """
        # 准备数据（计算指标）
        prepared_data = self.strategy.prepare_data(df)
        
        # 无论是否有信号，都输出当前最新的指标数据
        self.print_current_indicators(prepared_data)
        
        # 获取最新K线的时间
        if not prepared_data.empty:
            latest = prepared_data.iloc[-1]
            latest_time = latest['datetime'].strftime('%Y-%m-%d %H:%M')
            
            # 如果是新的K线，重置交易状态
            if self.current_candle_time != latest_time:
                self.current_candle_time = latest_time
                self.current_candle_had_entry = False
                self.current_candle_had_exit = False
                self.logger.info(f"新的K线开始，重置交易状态: {latest_time}")
        
        # 检查平仓信号（如果有持仓）
        if self.long_position or self.short_position:
            has_exit, exit_signal = await self.strategy.check_exit_signal(prepared_data, self.entry_data)
            if has_exit:
                exit_dt = pd.to_datetime(exit_signal['datetime'])
                position_type = "多头" if self.long_position else "空头"
                self.logger.info(f"[{exit_dt}] {position_type}平仓信号: {exit_signal['reason']}")
                self.logger.info(f"平仓价格: {exit_signal['price']:.6f}")
                
                # 执行平仓操作
                side = "sell" if self.long_position else "buy"
                order = await self.place_order(side=side, size=0, price=0, reduce_only=True)
                
                # 保存交易记录到数据库
                entry_trade_id = self.entry_data.get('trade_id') if self.entry_data else None
                trade_id = await self.save_trade_to_db(
                    order=order,
                    trade_type="exit",
                    reason=exit_signal.get('reason', ''),
                    entry_trade_id=entry_trade_id
                )
                
                # 计算盈利金额
                entry_price = self.entry_data.get('price', 0) if self.entry_data else 0
                
                # 先使用信号价格作为临时平仓价格
                temp_exit_price = exit_signal['price']
                
                # 如果下单成功，更新为实际成交价
                if order and 'fill_price' in order:
                    actual_exit_price = float(order.get('fill_price'))
                    exit_signal['original_price'] = temp_exit_price  # 保存原始信号价格
                    exit_signal['price'] = actual_exit_price  # 更新为实际成交价
                    self.logger.info(f"平仓使用实际成交价: {actual_exit_price} (原信号价格: {temp_exit_price})")
                
                # 使用最终价格进行计算
                exit_price = exit_signal['price']
                
                # 获取杠杆倍数
                leverage = self.strategy_params.get("leverage", 25)
                
                # 根据仓位方向计算盈亏
                if self.long_position:  # 多头盈亏 = (卖出价 - 买入价) / 买入价
                    profit_pct = ((exit_price - entry_price) / entry_price) * 100
                else:  # 空头盈亏 = (买入价 - 卖出价) / 买入价
                    profit_pct = ((entry_price - exit_price) / entry_price) * 100
                
                # 计算杠杆后的盈亏
                leveraged_profit_pct = profit_pct * leverage
                
                profit_emoji = "🔴" if profit_pct < 0 else "🟢"
                
                # 发送钉钉通知
                if order:
                    # 准备价格信息文本
                    price_info = f"💰 成交价: {self.strategy.base_format_price(exit_price)}"
                    if 'original_price' in exit_signal:
                        price_info += f"\n\n📊 信号价: {self.strategy.base_format_price(exit_signal['original_price'])}"
                    
                    notification_content = (
                        f"📊 信号:{self.symbol} {position_type}平仓\n\n"
                        f"⏰ 时间: {exit_dt}\n\n"
                        f"{price_info}\n\n"
                        f"{profit_emoji} 盈亏: {profit_pct:.2f}% (实际盈亏: {leveraged_profit_pct:.2f}%)\n\n"
                        f"📝 原因: {exit_signal['reason']}\n\n"
                        f"🔢 订单ID: {order.get('id', '未知')}"
                    )
                else:
                    notification_content = (
                        f"📊 信号:{self.symbol} {position_type}平仓\n\n"
                        f"⏰ 时间: {exit_dt}\n\n"
                        f"💰 价格: {self.strategy.base_format_price(exit_signal['price'])}\n\n"
                        f"{profit_emoji} 盈亏: {profit_pct:.2f}% (实际盈亏: {leveraged_profit_pct:.2f}%)\n\n"
                        f"📝 原因: {exit_signal['reason']}\n\n"
                        f"❌ 订单执行失败"
                    )
                
                await self.send_notification(f"🔔 {self.symbol} {position_type}平仓", notification_content)
                
                # 更新状态
                self.long_position = False
                self.short_position = False
                self.entry_data = None
                
                # 标记当前K线已有平仓
                self.current_candle_had_exit = True
                
                # 打印详细数据
                self.print_detailed_signal_data(prepared_data, "平仓", exit_signal)
                
                return
        
        # 如果没有持仓，且当前K线没有发生过"开仓后平仓"，才检查开仓信号
        if not self.long_position and not self.short_position:
            # 检查当前K线是否已经有过开仓和平仓
            if self.current_candle_had_exit:
                self.logger.info(f"当前K线 {self.current_candle_time} 已有开仓和平仓，跳过开仓信号检查")
                return
            
            has_entry, entry_signal = await self.strategy.check_entry_signal(prepared_data)
            if has_entry:
                entry_dt = pd.to_datetime(entry_signal['datetime'])
                position_type = "多头" if entry_signal['direction'] == '多' else "空头"
                
                self.logger.info(f"[{entry_dt}] {position_type}开仓信号: {entry_signal.get('reason', '')}")
                self.logger.info(f"开仓价格: {entry_signal['price']:.6f}")
                
                # 执行开仓操作
                side = "buy" if position_type == "多头" else "sell"
                # 计算仓位大小（简单示例，实际应根据风险管理调整）
                margin = self.strategy_params.get("margin_size", 30)  # 默认使用1个单位的保证金
                leverage = self.strategy_params.get("leverage", 25)  # 默认使用20倍杠杆
                size = self.strategy.calculate_position_size(
                    margin=margin,
                    leverage=leverage,
                    price=entry_signal['price']
                )
                self.logger.info(f"计算的仓位大小: {size}张合约 (保证金: {margin} USDT, 杠杆: {leverage}倍)")
                order = await self.place_order(side=side, size=size)
                
                # 保存交易记录到数据库
                trade_id = await self.save_trade_to_db(
                    order=order,
                    trade_type="entry",
                    reason=entry_signal.get('reason', '')
                )
                
                # 如果成功保存交易记录，在entry_data中添加交易ID
                if trade_id and entry_signal:
                    entry_signal['trade_id'] = trade_id
                
                # 新增: 如果订单成功，使用实际成交价更新entry_data和策略内部价格
                if order and 'fill_price' in order:
                    # 保存原始信号价格用于记录
                    original_price = entry_signal['price']
                    entry_signal['original_price'] = original_price
                    
                    actual_price = float(order.get('fill_price'))
                    entry_signal['price'] = actual_price  # 更新entry_data中的价格
                    
                    # 更新策略内部的价格记录，用于后续平仓计算
                    if position_type == "多头":
                        self.strategy.long_price = actual_price
                    else:
                        self.strategy.short_price = actual_price
                    
                    self.logger.info(f"使用实际成交价 {actual_price} 更新策略内部价格 (原信号价格: {original_price})")

                # 发送钉钉通知
                if order:
                    # 准备价格信息文本
                    price_info = f"💰 成交价: {self.strategy.base_format_price(order.get('fill_price'))}"
                    if 'original_price' in entry_signal:
                        price_info += f"\n\n📊 信号价: {self.strategy.base_format_price(entry_signal['original_price'])}"
                    
                    notification_content = (
                        f"📊 信号: {self.symbol}{position_type}开仓\n\n"
                        f"⏰ 时间: {entry_dt}\n\n"
                        f"{price_info}\n\n"
                        f"📝 原因: {entry_signal.get('reason', '')}\n\n"
                        f"🔢 订单ID: {order.get('id', '未知')}\n\n"
                        f"📈 仓位: {size}张"
                    )
                else:
                    notification_content = (
                        f"📊 信号: {self.symbol}{position_type}开仓\n\n"
                        f"⏰ 时间: {entry_dt}\n\n"
                        f"💰 价格: {self.strategy.base_format_price(entry_signal['price'])}\n\n"
                        f"📝 原因: {entry_signal.get('reason', '')}\n\n"
                        f"❌ 订单执行失败"
                    )
                
                await self.send_notification(f"🔔 {self.symbol} {position_type}开仓", notification_content)
                
                # 更新状态
                if position_type == "多头":
                    self.long_position = True
                else:
                    self.short_position = True
                self.entry_data = entry_signal
                
                # 标记当前K线已有开仓
                self.current_candle_had_entry = True
                
                # 打印详细数据
                self.print_detailed_signal_data(prepared_data, "开仓", entry_signal)
    
    def print_detailed_signal_data(self, prepared_data: pd.DataFrame, signal_type: str, signal_data: Dict[str, Any]):
        """
        打印详细的信号判断数据
        
        参数:
            prepared_data: 准备好的K线数据
            signal_type: 信号类型（开仓/平仓）
            signal_data: 信号数据
        """
        latest = prepared_data.iloc[-1]
        
        self.logger.info(f"--- {self.symbol} {signal_type}信号详细数据 ---")
        self.logger.info(f"时间: {latest['datetime']}")
        
        # 打印SAR指标值
        self.logger.info(f"SAR227(平仓): {latest['sar_exit']:.6f}")
        self.logger.info(f"SAR224(开仓1): {latest['sar_entry1']:.6f}")
        self.logger.info(f"SAR225(开仓2): {latest['sar_entry2']:.6f}")
        self.logger.info(f"SAR223(过滤): {latest['sar_filter']:.6f}")
        
        # 打印价格数据
        self.logger.info(f"价格: 开={latest['open']:.6f}, 高={latest['high']:.6f}, 低={latest['low']:.6f}, 收={latest['close']:.6f}")
        
        # 打印其他指标
        self.logger.info(f"ADX: {latest['adx']:.2f}, RVI: {latest['rvi']:.2f}")
        self.logger.info(f"SuperTrend趋势: {latest['supertrend_trend']}")
        
        # 打印信号特有数据
        if 'direction' in signal_data:
            self.logger.info(f"方向: {signal_data['direction']}")
        
        if 'reason' in signal_data:
            self.logger.info(f"原因: {signal_data['reason']}")
        
        if 'hard_stop_loss_price' in signal_data:
            self.logger.info(f"硬止损价: {signal_data['hard_stop_loss_price']:.6f} ({signal_data['hard_stop_loss_pct']:.2f}%)")
        
        self.logger.info("-------------------------------")
    
    def print_current_indicators(self, prepared_data: pd.DataFrame):
        """
        打印当前最新的指标数据，无论是否有信号
        
        参数:
            prepared_data: 准备好的K线数据
        """
        if prepared_data.empty:
            self.logger.warning("没有可用的指标数据")
            return
        
        latest = prepared_data.iloc[-1]
        
        self.logger.info(f"=== {self.symbol} 当前指标数据 ===")
        self.logger.info(f"时间: {latest['datetime']}")
        
        # 打印SAR指标值
        self.logger.info(f"SAR227(平仓): {latest['sar_exit']:.6f}")
        self.logger.info(f"SAR224(开仓1): {latest['sar_entry1']:.6f}")
        self.logger.info(f"SAR225(开仓2): {latest['sar_entry2']:.6f}")
        self.logger.info(f"SAR223(过滤): {latest['sar_filter']:.6f}")
        
        # 打印价格数据
        self.logger.info(f"价格: 开={latest['open']:.6f}, 高={latest['high']:.6f}, 低={latest['low']:.6f}, 收={latest['close']:.6f}")
        
        # 打印其他指标
        self.logger.info(f"ADX: {latest['adx']:.2f}, RVI: {latest['rvi']:.2f}")
        self.logger.info(f"SuperTrend趋势: {latest['supertrend_trend']}")
        
        # 打印开仓条件是否满足的判断结果
        self.logger.info(f"多头信号: {latest.get('long_signal', False)}")
        self.logger.info(f"空头信号: {latest.get('short_signal', False)}")
        
        # 其他过滤条件
        self.logger.info(f"趋势过滤: {latest.get('trend_filter', False)}")
        self.logger.info(f"ADX条件: {latest.get('adx_pass', False)} (ADX > 20)")
        self.logger.info(f"RVI条件: {latest.get('rvi_pass', False)} (|RVI-50| > 5)")
        
        self.logger.info("=============================")

    async def send_notification(self, title: str, content: str):
        """
        发送钉钉通知
        
        参数:
            title: 通知标题
            content: 通知内容
        """
        try:
            await send_dingtalk_message(title, content)
        except Exception as e:
            self.logger.error(f"发送通知失败: {e}")
    
    async def run(self):
        """运行交易机器人"""
        # 初始化API密钥和交易环境
        trading_setup_success = await self.setup_trading()
        if not trading_setup_success:
            self.logger.warning(f"{self.symbol} 交易环境设置失败，将继续运行但无法下单")
        
        # 初始化
        success = await self.initialize()
        if not success:
            self.logger.error(f"{self.symbol} 机器人初始化失败，退出...")
            return
        
        # 设置K线更新回调函数
        self.logger.info(f"{self.symbol} 机器人开始运行，等待K线更新...")
        
        # 订阅K线数据
        await self.kline_subscriber.subscribe(on_kline_update=self.on_kline_update)
        
        # 保持程序运行
        try:
            while True:
                await asyncio.sleep(60)  # 每分钟检查一次
        except asyncio.CancelledError:
            self.logger.info("任务被取消，正在清理资源...")
        finally:
            # 关闭所有连接
            try:
                self.exchange.close_all_ws_connections()
            except Exception as e:
                self.logger.error(f"关闭连接时出错: {e}")
            
    async def on_kline_update(self, df: pd.DataFrame):
        """
        处理K线更新的回调函数
        
        参数:
            df: 更新后的K线数据DataFrame
        """
        try:
            if df is not None and not df.empty:
                # 获取最新K线的时间
                latest_time = df['datetime'].iloc[-1]
                current_candle_time = latest_time.strftime('%Y-%m-%d %H:%M')
                
                # 如果是新的K线，重置交易状态
                if self.current_candle_time != current_candle_time:
                    self.current_candle_time = current_candle_time
                    self.current_candle_had_entry = False
                    self.current_candle_had_exit = False
                    self.logger.info(f"新的K线开始，重置交易状态: {current_candle_time}")
                
                # 获取最新价格
                self.latest_price = df['close'].iloc[-1]
                
                # 处理信号
                await self.check_signals(df)
                self.last_signal_check_time = datetime.now()
        except Exception as e:
            self.logger.error(f"处理K线更新时出错: {e}", exc_info=True)


async def load_strategy_configs() -> List[Dict[str, Any]]:
    """
    从数据库加载策略配置
    
    返回:
        策略配置列表
    """
    db = SessionLocal()
    try:
        logger.info("从数据库加载策略配置...")
        configs = db.query(StrategyConfig).filter(
            StrategyConfig.strategy_name == 'SARStrategy',
            StrategyConfig.is_active == 1
        ).all()
        
        result = []
        for config in configs:
            # 解析参数
            param_string = config.parameters_json
            try:
                # 1. 使用正则表达式移除注释并添加缺失的逗号
                cleaned_string = re.sub(r"((?:True|False|[\d.-]+|'[^']*'))(#.*?)(['}])", r"\1,\3", param_string)
                
                # 2. 使用ast.literal_eval安全地解析修复后的字符串为Python字典
                strategy_params = ast.literal_eval(cleaned_string)
                
                result.append({
                    'symbol': config.symbol,
                    'parameters': strategy_params
                })
                logger.info(f"成功加载 {config.symbol} 的策略配置")
            except Exception as e:
                logger.error(f"解析 {config.symbol} 的策略参数时出错: {e}")
        
        return result
    finally:
        db.close()


async def main():
    """主函数"""
    parser = argparse.ArgumentParser(description="SAR策略实盘交易脚本")
    parser.add_argument("--testnet", action="store_true", help="使用测试网络")
    parser.add_argument("--symbols", type=str, nargs='+', help="指定要交易的符号，例如: BTC-USDT ETH-USDT")
    args = parser.parse_args()
    
    # 加载策略配置
    configs = await load_strategy_configs()
    
    # 如果指定了交易对，则过滤配置
    if args.symbols:
        configs = [c for c in configs if c['symbol'] in args.symbols]
    
    if not configs:
        logger.error("没有找到有效的策略配置，退出...")
        return
    
    # 创建并运行交易机器人
    bots = []
    tasks = []
    
    for config in configs:
        bot = SARTradingBot(
            symbol=config['symbol'],
            strategy_params=config['parameters'],
            use_testnet=args.testnet
        )
        bots.append(bot)
        tasks.append(bot.run())
    
    # 同时运行所有机器人
    logger.info(f"启动 {len(bots)} 个交易机器人...")
    await asyncio.gather(*tasks)


if __name__ == "__main__":
    try:
        asyncio.run(main())
    except KeyboardInterrupt:
        logger.info("程序被用户中断")
    except Exception as e:
        logger.error(f"程序运行时出错: {e}", exc_info=True)
    finally:
        # 关闭所有连接
        gate_adapter = GateAdapter()
        gate_adapter.close_all_ws_connections()
        logger.info("程序退出") 